package cn.kgc.vue.shiro;

import cn.hutool.crypto.digest.DigestUtil;
import cn.kgc.vue.entity.Permission;
import cn.kgc.vue.entity.RolePers;
import cn.kgc.vue.entity.UserRole;
import cn.kgc.vue.mapper.PermissionMapper;
import cn.kgc.vue.mapper.RolePersMapper;
import cn.kgc.vue.mapper.UserRoleMapper;
import cn.kgc.vue.utils.IpUtil;
import cn.kgc.vue.utils.JWTUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.data.redis.core.StringRedisTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author 课工场
 * @date 2024/6/28
 * @description
 */
public class CustomerRealm extends AuthorizingRealm {

    @Resource
    private JWTUtil jwtUtil;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private RolePersMapper rolePersMapper;

    @Resource
    private UserRoleMapper userRoleMapper;

    @Resource
    private PermissionMapper permissionMapper;

    // 判定当前real是否支持传入的token类型   UnsupportedTokenException
    @Override
    public boolean  supports(AuthenticationToken token){
        return token instanceof JWTToken;
    }

    //  用户访问首先资源时  该方法被调用
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        Integer uid = (Integer) principals.getPrimaryPrincipal();
        // 1. 根据用户id  查询角色id  user_role
        LambdaQueryWrapper<UserRole> userRoleLambda = new QueryWrapper<UserRole>().lambda();
        userRoleLambda.eq(UserRole::getUserId,uid);

        List<Integer> rids = userRoleMapper.selectList(userRoleLambda).stream().map(userRole -> userRole.getRoleId())
                .collect(Collectors.toList());

        // 2. 根据角色id  查询权限id   role_permission
        LambdaQueryWrapper<RolePers> rolePersLambda = new QueryWrapper<RolePers>().lambda();
        rolePersLambda.in(RolePers::getRoleId,rids);
        Set<Integer> pIds = rolePersMapper.selectList(rolePersLambda).stream()
                .map(rolePers -> rolePers.getPerId())
                .collect(Collectors.toSet());
        // 3. 根据权限id  查询权限字符串
        LambdaQueryWrapper<Permission> permissionLambda = new QueryWrapper<Permission>().lambda();
        permissionLambda.in(Permission::getId,pIds)
                        .eq(Permission::getIsMenu,2);
        List<String> permissions = permissionMapper.selectList(permissionLambda)
                .stream().map(permission -> permission.getPermission())
                .collect(Collectors.toList());

        // 4.将权限字符创封装 返回
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addStringPermissions(permissions);

        return simpleAuthorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {


        JWTToken jwtToken = (JWTToken) token;
        HttpServletRequest request = (HttpServletRequest) jwtToken.getRequest();

        String tokenPrincipal = (String) jwtToken.getPrincipal();

        // 校验token
        jwtUtil.verify(tokenPrincipal);
        // token   ip
        String md5HexKey = DigestUtil.md5Hex(tokenPrincipal);
        String redisIp = stringRedisTemplate.opsForValue().get(md5HexKey);
        // 比较当前请求的ip和redisIp是否相同  不同 则token盗用
        String ip = IpUtil.getIp(request);
        if (!ip.equals(redisIp)){
            throw  new RuntimeException("token非法,请重新认证");
        }

        // 解析 token   uid
        Map<String, Object> claims = jwtUtil.getClaims(tokenPrincipal);
        Object uid = claims.get("uid");

        // 用户合法   simpleAuthenticationInfo   比较密码     login(token) jwtToken    simpleAuthenticationInfo  token
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(uid ,jwtToken.getCredentials(), this.getName());
        return simpleAuthenticationInfo;
    }
}
